﻿using log4net;
using log4net.Core;
using Masuit.Tools;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;
using SharpCompress.Common;
using WebService.Models;
using System.Linq;
using System.Net;
using System.Text;
using Microsoft.AspNetCore.Http;
using YaseiAkira.AJ.Util;

namespace WebService.Filter
{
    public class CustomerExceptionFilter : IAsyncExceptionFilter
    {
        public class ExceptionCode
        {
            public string Code { get; set; }
            public Type ExType { get; set; }
            public string Desc { get; set; }

            public override string ToString()
            {
                return $"{Code}:{Desc}";
            }
        }

        private static readonly List<ExceptionCode> _errCodes = new()
        {
            new ExceptionCode {ExType=typeof(NullReferenceException), Code ="1000", Desc="空引用" },
            new ExceptionCode {ExType=typeof(IndexOutOfRangeException), Code = "2000", Desc="索引超出可用范围" },
            new ExceptionCode {ExType=typeof(NotImplementedException), Code = "3000", Desc="尚未实现处理逻辑" },
            new ExceptionCode {ExType=typeof(FileNotFoundException), Code = "4000", Desc="未找到指定资源文件" },
            new ExceptionCode {ExType=typeof(ArgumentException), Code = "5000", Desc="参数错误" },
            new ExceptionCode {ExType=typeof(DbUpdateException), Code = "6000", Desc="数据库操作失败" },
            new ExceptionCode {ExType=typeof(InvalidOperationException), Code = "7000", Desc="无效操作" },
            //new ExceptionCode {ExType=typeof(SqlException), Code = "8000", Desc="数据库异常" },
            new ExceptionCode {ExType=typeof(OutOfMemoryException), Code = "9000", Desc="内存溢出" },
        };

        private static readonly ExceptionCode _unknownExpCode = new()
        {
            Code = "-1",
            Desc = "未知错误"
        };

        public static ExceptionCode GetExceptionCode(Exception exception)
        {
            var exType = exception.GetType();

            return _errCodes.FirstOrDefault(p => exType == p.ExType) ?? _unknownExpCode;
        }

        public async Task OnExceptionAsync(ExceptionContext context)
        {
            var request = context.HttpContext.Request;

            if (context.Exception is APIFriendlyErrorException friendlyEx)
            {
                context.Result = new JsonResult(new
                {
                    Success = false,
                    friendlyEx.Message,
                });
                context.ExceptionHandled = true;
                return;
            }

            var msg = new JObject
            {
                { "path", request.Path.Value },
                { "query", request.QueryString.Value }
            };

            var msgText = context.Exception.Message;
            var errCode = GetExceptionCode(context.Exception);

            try
            {
                var headerDic = new Dictionary<string, string>();
                foreach (var header in request.Headers)
                {
                    headerDic.Add(header.Key, header.Value.ToString());
                }

                msg.Add("headers", JToken.FromObject(headerDic));

                if (context.Exception.InnerException != null)
                {
                    msgText += $"\r\ndetail:{context.Exception.InnerException.Message}";
                }


                if (context.Exception is DbUpdateException dbEx)
                {
                    msgText += $"DbErrors:{string.Join(";\r\n", dbEx.Entries.Select(e => e.ToString()))}";
                }

                msg.Add("errorMsg", msgText);

                if (request.Method.Equals("post", StringComparison.OrdinalIgnoreCase))
                {
                    if (request.Body != null
                        && !request.ContentType.ToLower().Contains("form-data"))
                    {
                        request.EnableBuffering();
                        using var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true);

                        var body = await reader.ReadToEndAsync();

                        msg.Add("body", body);

                        request.Body.Seek(0, SeekOrigin.Begin);
                    }
                }
            }
            catch
            {
            }

            await Task.Run(() =>
            {
                Util.Log4Logger
                .Error($"请求发生异常:{msg.ToString(Newtonsoft.Json.Formatting.Indented)}", context.Exception);
            });

            ActionResult result = null;

            var hasResponse = context.HttpContext != null && context.HttpContext.Response != null;

            if (hasResponse
                    && context.HttpContext.Response.StatusCode == (int)HttpStatusCode.Unauthorized)
            {
                result = new JsonResult(new
                {
                    Success = false,
                    Message = $"该请求需要登录或未授权:401",
                    NeedLogin = true
                });
            }
            else
            {
                result = new JsonResult(new ProcessResult
                {
                    Success = false,
                    Message = $"请求发生异常:{errCode}",
                    Data = string.Empty
                });
            }

            if (hasResponse)
            {
                context.HttpContext.Response.StatusCode = (int)HttpStatusCode.OK;
            }

            context.Result = result;
            context.ExceptionHandled = true;
        }
    }
}
